// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_OBJECTS_JS_ARRAY_H_
#define V8_OBJECTS_JS_ARRAY_H_

#include "src/objects/allocation-site.h"
#include "src/objects/fixed-array.h"
#include "src/objects/js-objects.h"
#include "torque-generated/class-definitions-from-dsl.h"

// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"

namespace v8 {
namespace internal {

    // The JSArray describes JavaScript Arrays
    //  Such an array can be in one of two modes:
    //    - fast, backing storage is a FixedArray and length <= elements.length();
    //       Please note: push and pop can be used to grow and shrink the array.
    //    - slow, backing storage is a HashTable with numbers as keys.
    class JSArray : public JSObject {
    public:
        // [length]: The length property.
        DECL_ACCESSORS(length, Object)

        // Overload the length setter to skip write barrier when the length
        // is set to a smi. This matches the set function on FixedArray.
        inline void set_length(Smi length);

        static bool HasReadOnlyLength(Handle<JSArray> array);
        static bool WouldChangeReadOnlyLength(Handle<JSArray> array, uint32_t index);

        // Initialize the array with the given capacity. The function may
        // fail due to out-of-memory situations, but only if the requested
        // capacity is non-zero.
        V8_EXPORT_PRIVATE static void Initialize(Handle<JSArray> array, int capacity,
            int length = 0);

        // If the JSArray has fast elements, and new_length would result in
        // normalization, returns true.
        bool SetLengthWouldNormalize(uint32_t new_length);
        static inline bool SetLengthWouldNormalize(Heap* heap, uint32_t new_length);

        // Initializes the array to a certain length.
        inline bool AllowsSetLength();

        V8_EXPORT_PRIVATE static void SetLength(Handle<JSArray> array,
            uint32_t length);

        // Set the content of the array to the content of storage.
        static inline void SetContent(Handle<JSArray> array,
            Handle<FixedArrayBase> storage);

        // ES6 9.4.2.1
        V8_WARN_UNUSED_RESULT static Maybe<bool> DefineOwnProperty(
            Isolate* isolate, Handle<JSArray> o, Handle<Object> name,
            PropertyDescriptor* desc, Maybe<ShouldThrow> should_throw);

        static bool AnythingToArrayLength(Isolate* isolate,
            Handle<Object> length_object,
            uint32_t* output);
        V8_WARN_UNUSED_RESULT static Maybe<bool> ArraySetLength(
            Isolate* isolate, Handle<JSArray> a, PropertyDescriptor* desc,
            Maybe<ShouldThrow> should_throw);

        // Support for Array.prototype.join().
        // Writes a fixed array of strings and separators to a single destination
        // string. This helpers assumes the fixed array encodes separators in two
        // ways:
        //   1) Explicitly with a smi, whos value represents the number of repeated
        //      separators.
        //   2) Implicitly between two consecutive strings a single separator.
        //
        // Here are some input/output examples given the separator string is ',':
        //
        //   [1, 'hello', 2, 'world', 1] => ',hello,,world,'
        //   ['hello', 'world']          => 'hello,world'
        //
        // To avoid any allocations, this helper assumes the destination string is the
        // exact length necessary to write the strings and separators from the fixed
        // array.
        // Since this is called via ExternalReferences, it uses raw Address values:
        // - {raw_fixed_array} is a tagged FixedArray pointer.
        // - {raw_separator} and {raw_dest} are tagged String pointers.
        // - Returns a tagged String pointer.
        static Address ArrayJoinConcatToSequentialString(Isolate* isolate,
            Address raw_fixed_array,
            intptr_t length,
            Address raw_separator,
            Address raw_dest);

        // Checks whether the Array has the current realm's Array.prototype as its
        // prototype. This function is best-effort and only gives a conservative
        // approximation, erring on the side of false, in particular with respect
        // to Proxies and objects with a hidden prototype.
        inline bool HasArrayPrototype(Isolate* isolate);

        DECL_CAST(JSArray)

        // Dispatched behavior.
        DECL_PRINTER(JSArray)
        DECL_VERIFIER(JSArray)

        // Number of element slots to pre-allocate for an empty array.
        static const int kPreallocatedArrayElements = 4;

        DEFINE_FIELD_OFFSET_CONSTANTS(JSObject::kHeaderSize,
            TORQUE_GENERATED_JSARRAY_FIELDS)

        static const int kLengthDescriptorIndex = 0;

        // Max. number of elements being copied in Array builtins.
        static const int kMaxCopyElements = 100;

        // This constant is somewhat arbitrary. Any large enough value would work.
        static const uint32_t kMaxFastArrayLength = 32 * 1024 * 1024;

        // Min. stack size for detecting an Array.prototype.join() call cycle.
        static const uint32_t kMinJoinStackSize = 2;

        static const int kInitialMaxFastElementArray = (kMaxRegularHeapObjectSize - FixedArray::kHeaderSize - kSize - AllocationMemento::kSize) >> kDoubleSizeLog2;

        // Valid array indices range from +0 <= i < 2^32 - 1 (kMaxUInt32).
        static const uint32_t kMaxArrayIndex = kMaxUInt32 - 1;

        OBJECT_CONSTRUCTORS(JSArray, JSObject);
    };

    Handle<Object> CacheInitialJSArrayMaps(Handle<Context> native_context,
        Handle<Map> initial_map);

    // The JSArrayIterator describes JavaScript Array Iterators Objects, as
    // defined in ES section #sec-array-iterator-objects.
    class JSArrayIterator : public JSObject {
    public:
        DECL_PRINTER(JSArrayIterator)
        DECL_VERIFIER(JSArrayIterator)

        DECL_CAST(JSArrayIterator)

        // [iterated_object]: the [[IteratedObject]] inobject property.
        DECL_ACCESSORS(iterated_object, Object)

        // [next_index]: The [[ArrayIteratorNextIndex]] inobject property.
        // The next_index is always a positive integer, and it points to
        // the next index that is to be returned by this iterator. It's
        // possible range is fixed depending on the [[iterated_object]]:
        //
        //   1. For JSArray's the next_index is always in Unsigned32
        //      range, and when the iterator reaches the end it's set
        //      to kMaxUInt32 to indicate that this iterator should
        //      never produce values anymore even if the "length"
        //      property of the JSArray changes at some later point.
        //   2. For JSTypedArray's the next_index is always in
        //      UnsignedSmall range, and when the iterator terminates
        //      it's set to Smi::kMaxValue.
        //   3. For all other JSReceiver's it's always between 0 and
        //      kMaxSafeInteger, and the latter value is used to mark
        //      termination.
        //
        // It's important that for 1. and 2. the value fits into the
        // Unsigned32 range (UnsignedSmall is a subset of Unsigned32),
        // since we use this knowledge in the fast-path for the array
        // iterator next calls in TurboFan (in the JSCallReducer) to
        // keep the index in Word32 representation. This invariant is
        // checked in JSArrayIterator::JSArrayIteratorVerify().
        DECL_ACCESSORS(next_index, Object)

        // [kind]: the [[ArrayIterationKind]] inobject property.
        inline IterationKind kind() const;
        inline void set_kind(IterationKind kind);

        DEFINE_FIELD_OFFSET_CONSTANTS(JSObject::kHeaderSize,
            TORQUE_GENERATED_JSARRAY_ITERATOR_FIELDS)

        OBJECT_CONSTRUCTORS(JSArrayIterator, JSObject);
    };

} // namespace internal
} // namespace v8

#include "src/objects/object-macros-undef.h"

#endif // V8_OBJECTS_JS_ARRAY_H_
